﻿using System;
using System.Collections;
using System.Collections.Generic;
using GBricks.Collections;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace GBricks.Tests.Collections
{
  [TestClass]
  public class HashCodeTests
  {
    [TestMethod]
    public void HashCodeTemp() {
      var hashCode = new HashCode();
      hashCode.ToString();
      IEnumerable e = hashCode;
      e.GetEnumerator();
    }

    [TestMethod]
    public void HashCodeRotateRight() {
      const int Value = 7;
      var result = HashCode.RotateRight(Value, ~0x1F);
      Assert.AreEqual(result, Value);
    }

    [TestMethod]
    public void HashCodeCalculate() {
      Calculate(true, false, Byte.MinValue, Byte.MaxValue, SByte.MinValue, SByte.MaxValue, default(object), new object());
      Calculate(Int16.MinValue, Int16.MaxValue, UInt16.MinValue, UInt16.MaxValue, Int32.MinValue, Int32.MaxValue, UInt32.MinValue, UInt32.MaxValue);
      Calculate(Int64.MinValue, Int64.MaxValue, UInt64.MinValue, UInt64.MaxValue, DateTime.MinValue, DateTime.MaxValue, DateTime.Now, DateTime.UtcNow);
      Calculate(Decimal.MinValue, Decimal.MinusOne, Decimal.Zero, Decimal.One, Decimal.MaxValue, String.Empty, default(string), "qqq");
      Calculate(Single.MinValue, Single.MinValue, Single.NegativeInfinity, Single.PositiveInfinity, Single.NaN, Single.Epsilon, 0f, 1f);
      Calculate(Double.MinValue, Double.MinValue, Double.NegativeInfinity, Double.PositiveInfinity, Double.NaN, Double.Epsilon, 0d, 1d);
    }

    private void Calculate<T1, T2, T3, T4, T5, T6, T7, T8>(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8) {
      Assert.AreEqual(HashCode.Calculate(v1), HashCode.Calculate(v1), "HashCode.Calculate`1<{0}>", typeof(T1));
      Assert.AreEqual(HashCode.Calculate(v1, v2), HashCode.Calculate(v1, v2), "HashCode.Calculate`2<{0}, {1}>", typeof(T1), typeof(T2));
      Assert.AreEqual(HashCode.Calculate(v1, v2, v3), HashCode.Calculate(v1, v2, v3), "HashCode.Calculate`3<{0}, {1}, {2}>", typeof(T1), typeof(T2), typeof(T3));
      Assert.AreEqual(HashCode.Calculate(v1, v2, v3, v4), HashCode.Calculate(v1, v2, v3, v4), "HashCode.Calculate`4<{0}, {1}, {2}, {3}>", typeof(T1), typeof(T2), typeof(T3), typeof(T4));
      Assert.AreEqual(HashCode.Calculate(v1, v2, v3, v4, v5), HashCode.Calculate(v1, v2, v3, v4, v5), "HashCode.Calculate`5<{0}, {1}, {2}, {3}, {4}>", typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5));
      Assert.AreEqual(HashCode.Calculate(v1, v2, v3, v4, v5, v6), HashCode.Calculate(v1, v2, v3, v4, v5, v6), "HashCode.Calculate`6<{0}, {1}, {2}, {3}, {4}, {5}>", typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6));
      Assert.AreEqual(HashCode.Calculate(v1, v2, v3, v4, v5, v6, v7), HashCode.Calculate(v1, v2, v3, v4, v5, v6, v7), "HashCode.Calculate`7<{0}, {1}, {2}, {3}, {4}, {5}, {6}>", typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7));
      Assert.AreEqual(HashCode.Calculate(v1, v2, v3, v4, v5, v6, v7, v8), HashCode.Calculate(v1, v2, v3, v4, v5, v6, v7, v8), "HashCode.Calculate`8<{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}>", typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8));

      Assert.AreEqual(HashCode.CalculateWith(v1, EqualityComparer<T1>.Default), HashCode.CalculateWith(v1, EqualityComparer<T1>.Default), "HashCode.CalculateWith<{0}>", typeof(T1));
      Assert.AreEqual(HashCode.CalculateWith(v1, EqualityComparer<T1>.Default, v2, EqualityComparer<T2>.Default), HashCode.CalculateWith(v1, EqualityComparer<T1>.Default, v2, EqualityComparer<T2>.Default), "HashCode.CalculateWith<{0}, {1}>", typeof(T1), typeof(T2));
    }

    [TestMethod]
    public void HashCodeCalculateWith() {
      var NullComparer = default(IEqualityComparer<string>);
      var NullProvider1 = default(Func<string, int>);
      var NullProvider2 = default(Func<string, int, int>);

      Assert.AreEqual(HashCode.CalculateWith("a", NullComparer), HashCode.CalculateWith("a", StringComparer.Ordinal));
      Assert.AreEqual(HashCode.CalculateWith(null, NullComparer), HashCode.CalculateWith(null, StringComparer.Ordinal));
      Assert.AreEqual(HashCode.CalculateWith("a", StringComparer.OrdinalIgnoreCase), HashCode.CalculateWith("A", StringComparer.OrdinalIgnoreCase));
      Assert.AreEqual(HashCode.CalculateWith("a", StringComparer.OrdinalIgnoreCase, "B", StringComparer.InvariantCultureIgnoreCase), HashCode.CalculateWith("A", StringComparer.OrdinalIgnoreCase, "b", StringComparer.InvariantCultureIgnoreCase));

      Assert.AreEqual(HashCode.CalculateWith(null, (string item) => 0), HashCode.CalculateWith(null, (string item) => 1));
      Assert.AreEqual(HashCode.CalculateWith(null, NullProvider1), HashCode.CalculateWith(null, NullProvider2, 0));
      Assert.AreEqual(HashCode.CalculateWith("a", NullProvider1), HashCode.CalculateWith("a", NullProvider2, 0));
    }

    [TestMethod]
    public void HashCodeCalculateAll() {
      var lower = new[] { "a", "b", null };
      var lower2 = new[] { "a", "b", null };
      var upper = new[] { "A", "B", null, };

      var NullComparer = default(IEqualityComparer<string>);
      var NullProvider1 = default(Func<string, int>);
      var NullProvider2 = default(Func<string, int, int>);

      Assert.AreEqual(HashCode.CalculateAll(lower, NullComparer), HashCode.CalculateAll(lower, EqualityComparer<string>.Default));
      Assert.AreEqual(HashCode.CalculateAll(lower, StringComparer.OrdinalIgnoreCase), HashCode.CalculateAll(upper, StringComparer.OrdinalIgnoreCase));
      Assert.AreEqual(HashCode.CalculateAll(lower, item => StringComparer.InvariantCultureIgnoreCase.GetHashCode(item)), HashCode.CalculateAll(upper, (item, index) => StringComparer.InvariantCultureIgnoreCase.GetHashCode(item)));

      var empty = new string[0];
      Assert.AreEqual(HashCode.CalculateAll(null, NullComparer), HashCode.CalculateAll(empty, NullComparer));
      Assert.AreEqual(HashCode.CalculateAll(null, NullProvider1), HashCode.CalculateAll(empty, NullProvider1));
      Assert.AreEqual(HashCode.CalculateAll(null, NullProvider2), HashCode.CalculateAll(empty, NullProvider2));
    }
  }
}
